package http.annotation;

import http.executor.ResponseExecutor;
import http.util.ReflectionUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class ExceptionHandlerMethodResolver implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);

    @EventListener(ContextRefreshedEvent.class)
    public void initExceptionHandler(){
        Map<String, Object> annotations = applicationContext.getBeansWithAnnotation(ControllerAdvice.class);
        for (Map.Entry<String, Object> entry : annotations.entrySet()) {
            List<Method> methodsList = ReflectionUtil.getMethodByAnnotation(entry.getValue().getClass(), ExceptionHandler.class);
            for (Method method : methodsList) {
                ExceptionHandler annotation = method.getAnnotation(ExceptionHandler.class);
                for (Class<? extends Throwable> clazz : annotation.value()) {
                    mappedMethods.put(clazz,method);
                }
            }

        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    public void resolve(Exception e, String id){
        for (Map.Entry<Class<? extends Throwable>, Method> entry : mappedMethods.entrySet()) {
            if(entry.getKey().isAssignableFrom(e.getClass())){
                Object handler = applicationContext.getBean(entry.getValue().getDeclaringClass());
                try {
                    Object result = entry.getValue().invoke(handler, e);
                    ResponseExecutor.putResult(id,result);
                    break;
                } catch (IllegalAccessException e1) {
                    e1.printStackTrace();
                } catch (InvocationTargetException e1) {
                    e1.printStackTrace();
                }
                System.out.println("找到能处理的类了");
            }
        }
    }
}
